import java.io.*;
import java.net.*;
import java.util.*;
import org.xml.sax.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import org.xml.sax.helpers.XMLReaderFactory;
import org.safehaus.uuid.*;

/*
	UUID Generation Code
	GET Code
	Do events need to be in order?
*/


class ServiceInterface implements Runnable {
	private Socket client;
	private String log_file = "log/status.log_" + getDate();
	private Hashtable request_ht = new Hashtable();
	private Hashtable response_ht = new Hashtable();
	private String ics_path = "";
	private String message = "";	

	ServiceInterface(Socket client,String ics_path) {
		this.ics_path = ics_path;
		this.client = client;
	}

	private String getDate() {
		DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date date = new Date();
		return dateFormat.format(date);
	}

	private String getDateTime() {
		DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
		Date date = new Date();
		return dateFormat.format(date);
	}
	
	public void run(){
		String line = "";
		BufferedReader in = null;
		PrintWriter out = null;
		BufferedWriter log_writer = null;
		try{
			log_writer = new BufferedWriter(new FileWriter(log_file,true));
			in = new BufferedReader(new InputStreamReader(client.getInputStream()));
			out = new PrintWriter(client.getOutputStream(), true);
		} catch (IOException e) {
			try {
				log_writer.write("Failed to establish input and output streams."); log_writer.newLine();
				client.close();
			} catch (IOException ex) {
			}
		}
		request_ht.put("Content-Length",0);
		Vector input_lines = read_lines(in,log_writer);
		int http_code = process_input(input_lines);
		if (http_code == 100 && (request_ht.get("type").equals("GET"))) {
			message = return_file();
			http_code = 200;
			outputResponse(http_code,out);
		} else if (http_code == 100) {
			System.out.println("Content Length: " + request_ht.get("Content-Length"));
			out.println("HTTP/1.1 100 Continue"); 
			out.println("");
			String data_lines = read_body(in,log_writer);
			http_code = process_content(data_lines,out,log_writer);
			message = "";
			outputResponse(http_code,out);
		} else {
			http_code = header_message(http_code,out);	
		} 
		try {
			client.close();	
			log_writer.close();
		} catch (IOException s_error) {	
			s_error.printStackTrace();
		}
	}

	public String get_ics_headers() {
		String headers = "";
		headers += "BEGIN:VCALENDAR\n";
		headers += "VERSION:2.0\n";
		headers += "PRODID:-//presers.org.uk/NONSGML Preserv CalInt v0.1//EN\n";
		return headers;
	}

	public String get_ics_footers() {
		String footers = "END:VCALENDAR";
		return footers;
	}
	
	public String return_file() {
		String file = String.valueOf(request_ht.get("uri"));
		if (ics_path.substring(ics_path.length()-1,ics_path.length()).equals("/")) {
			ics_path = ics_path.substring(0,ics_path.length()-1);
		}
		String filename = ics_path + file;
		String total = "";
		try {
			BufferedReader in = new BufferedReader(new FileReader(filename));
			String str;
			while ((str = in.readLine()) != null) {
				total += str + "\n";
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return total;
	}

	public int process_content(String input_lines, PrintWriter out, BufferedWriter log_writer) {	
		String file = String.valueOf(request_ht.get("uri"));
		if (ics_path.substring(ics_path.length()-1,ics_path.length()).equals("/")) {
			ics_path = ics_path.substring(0,ics_path.length()-1);
		}
		String filename = ics_path + file;
		File ics_file = new File(filename);
		System.out.println("Filename: " + filename);
				
		Hashtable tag_parts = new Hashtable();
		Vector keys = new Vector();
		String[] lines = input_lines.split("\n");
		for (int i=0;i<lines.length;i++) {
			String first = (lines[i].split(":"))[0].trim();
			String rest = lines[i].substring(lines[i].indexOf(":")+1,lines[i].length());
			keys.add(first);
			tag_parts.put(first,rest);
		}

		if (ics_file.exists()) {
			
			if (request_ht.containsKey("If-Match") && tag_parts.containsKey("UID")) {
				System.out.println("In Routine");
				String uid = (String)tag_parts.get("UID");
				String total = "";
				Vector events = new Vector();
				try {
					BufferedReader in = new BufferedReader(new FileReader(filename));
					String str;
					String evt = "";
					String event = "";
					boolean flag = false;
					while ((str = in.readLine()) != null) {
						while (flag == false) {
							if (str.equals("BEGIN:VEVENT")) {
								flag = true;
								events.add(evt);
								evt = "";
							} else {
								evt += str + "\n";
								str = in.readLine();
							}
						}
						if (str.equals("END:VEVENT")) {
							if (event.length() > 1) {
								events.add(event);
							} else {
								evt += "END:VEVENT\n";
								events.add(evt);
							}
							evt = "";
							event = "";
						} else {
							String[] parts = str.split(":",2);
							if (parts[0].equals("UID")) {
								String localuid = parts[1].trim();
								uid = uid.trim();
								if (localuid.equals(uid)) {
									for (Enumeration e=keys.elements(); e.hasMoreElements();) {
										String key = (String)e.nextElement();
										String value = (String)tag_parts.get(key);
										event += key + ":" + value + "\n";
									}
								} else {
									evt += str + "\n";
								}
							} else {
								evt += str + "\n";
							}
						}
					}
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					BufferedWriter ics_out = new BufferedWriter(new FileWriter(filename));
					for (Enumeration e=events.elements(); e.hasMoreElements();) {
						String event = (String)e.nextElement();
						ics_out.write(event);
					}
					ics_out.write("END:VCALENDAR");
					ics_out.close();
				} catch (IOException ioe) {
					ioe.printStackTrace();
				}
			} else {
				if (!(tag_parts.containsKey("UID"))) {
					String uid = generate_uuid();
					tag_parts.put("UID",uid);
					keys.add(2,"UID");
				}
				String event = "";
				for (Enumeration e=keys.elements(); e.hasMoreElements();) {
					String key = (String)e.nextElement();
					String value = (String)tag_parts.get(key);
					event += key + ":" + value + "\n";
				}
				String total = "";	
				try {
					BufferedReader in = new BufferedReader(new FileReader(filename));
					String str;
					while ((str = in.readLine()) != null) {
						if (str.trim().equals("END:VCALENDAR")) {
							total += event;
						}
						total += str + "\n";
					}
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					BufferedWriter ics_out = new BufferedWriter(new FileWriter(filename));
					ics_out.write(total);
					ics_out.close();
				} catch (IOException ioe) {
					ioe.printStackTrace();
				}
			}
		} else {
			try {
				BufferedWriter ics_out = new BufferedWriter(new FileWriter(filename));
				ics_out.write(get_ics_headers());
				
				if (((String)tag_parts.get("UID")).equals("") || tag_parts.get("UID") == null) {
					String uid = generate_uuid();
					tag_parts.put("UID",uid);
					keys.add(2,"UID");
				}
		
				for (Enumeration e=keys.elements(); e.hasMoreElements();) {
					String key = (String)e.nextElement();
					String value = (String)tag_parts.get(key);
					ics_out.write(key + ":" + value + "\n");
				}
	
				ics_out.write(get_ics_footers());
				ics_out.close();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
		System.out.println(ics_path);	
		return 202;
	}
	
	public String read_body(BufferedReader in, BufferedWriter log_writer) {
		int clength = Integer.parseInt(request_ht.get("Content-Length").toString().trim());
		int ccount = 0;
		String body = "";
		while (ccount<clength) {
			try {
				char current = (char)in.read();
				body += current;
				ccount++;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return body;
	}
		
	public Vector read_lines(BufferedReader in, BufferedWriter log_writer) {
		String line = "";
		Vector input_lines = new Vector();
		int chars = 0;
		try {
			log_writer.write("Processing Request from " + client.getInetAddress().toString() + " (" + getDateTime() + ")"); log_writer.newLine();
			line = in.readLine();
		} catch (IOException e) {
			try {
				log_writer.write("Read from " + client.getInetAddress().toString() +" at " + getDateTime() + " failed"); log_writer.newLine();
				client.close();
			} catch (IOException ex) {
			}
		}
		while(!line.equals("")){
			try{
				input_lines.add(line);
				log_writer.write(line); log_writer.newLine();
				line = in.readLine();
			} catch (IOException e) {
				try {
					log_writer.write("Read from " + client.getInetAddress().toString() +" at " + getDateTime() + " failed"); log_writer.newLine();
					client.close();
				} catch (IOException ex) {
				}
			}
		}
		return input_lines;
	}

	private int process_input(Vector input_lines) {
		String line = (String)input_lines.get(0);
		String[] request = line.split(" ");
		if (request[0].equalsIgnoreCase("HEAD") || request[0].equalsIgnoreCase("GET") || request[0].equalsIgnoreCase("POST") || request[0].equalsIgnoreCase("PUT") || request[0].equalsIgnoreCase("DELETE") || request[0].equalsIgnoreCase("TRACE") || request[0].equalsIgnoreCase("OPTIONS") || request[0].equalsIgnoreCase("CONNECT")) {
			request_ht.put("type",request[0]);
			request_ht.put("uri",request[1]);
			request_ht.put("protocal",request[2]);
			Enumeration e = input_lines.elements();
			e.nextElement();
			while (e.hasMoreElements()) {
				line = (String)e.nextElement();
				System.out.println(line);
				request = line.split(":",2);	
				request_ht.put(request[0],request[1]);
			}
			try {
				if (!request_ht.get("uri").toString().contains(".ics")) {
					return 400;					
				}
				if (!request_ht.get("Content-Type").toString().trim().equalsIgnoreCase("text/icalendar")) {
					return 415;
				}
				return 100;
			} catch (Exception ex) {
				ex.printStackTrace();
				return 400;
			}
		} else {
			return 400;
		}
	}
	private int header_message(int http_code, PrintWriter out) {
		switch (http_code) {
			case 100: out.println("HTTP/1.1 100 Continue"); outputResponse(100,out); break;
			case 200: out.println("HTTP/1.1 200 OK"); break;
			case 302: out.println("HTTP/1.1 302 Found"); outputResponse(302,out); break;
			case 307: out.println("HTTP/1.1 307 Temporary Redirect"); outputResponse(307,out); break;
			case 404: out.println("HTTP/1.1 404 Not Found");
			case 415: out.println("HTTP/1.1 415 Unsupported Media Type");
			default: out.println("HTTP/1.1 400 Bad Request"); break;
		}
		//out.println("Date: " + getDateTime());
		//out.println("Server: Service Controller");
		//out.println("X-Powered-By: Java");
		//out.println("Connection: close");
		//out.println("Content-Type: text/xml; charset=utf-8");
		out.println("");
		return http_code;
	}
	private void outputResponse(int http_code,PrintWriter out) {
		switch (http_code) {
			case 100: out.println("HTTP/1.1 100 Continue"); outputResponse(100,out); break;
			case 200: out.println("HTTP/1.1 200 OK"); break;
			case 202: out.println("HTTP/1.1 202 Accepted"); break;
			case 302: out.println("HTTP/1.1 302 Found"); outputResponse(302,out); break;
			case 307: out.println("HTTP/1.1 307 Temporary Redirect"); outputResponse(307,out); break;
			case 404: out.println("HTTP/1.1 404 Not Found");
			case 415: out.println("HTTP/1.1 415 Unsupported Media Type");
			default: out.println("HTTP/1.1 400 Bad Request"); break;
		}
		out.println("Date: " + getDateTime());
		out.println("Server: Service Controller");
		out.println("X-Powered-By: Java");
		out.println("Connection: close");
		if (http_code == 302 || http_code == 307) {
			out.println("Location: " + response_ht.get("location"));
		}
		if (!message.equals("")) {
			out.println("Content-Type: text/icalendar; charset=utf-8");
			out.println("Content-Length: " + message.toCharArray().length);
			out.println("");
			out.println(message);
			System.out.println(message);
		} else {
			out.println("Content-Type: text/icalendar; charset=utf-8");
			out.println("Content-Length: 0");
		}
	}
	
	public String generate_uuid() {
		UUIDGenerator foo = UUIDGenerator.getInstance();
		org.safehaus.uuid.UUID uuid = foo.generateTimeBasedUUID();
		String uuid_string = uuid.toString();
		return uuid_string;
	}
}

class SocketThrdServer {

	ServerSocket server = null;
	static String ics_path = "";

	SocketThrdServer(){
		/**
		 * Constructor
		 * In here you can do things like construct a UI or something, but since each thread will handle things differently we don't want a master interfacce.
		 */
	} 

	public void listenSocket(int port){
		try{
			server = new ServerSocket(port);
			System.out.println("Listening on port " + port);
		} catch (IOException e) {
			System.out.println("Could not listen on port " + port);
			System.exit(-1);
		}
		while(true){
			ServiceInterface si;
			try{
				si = new ServiceInterface(server.accept(),ics_path) ;
				Thread sit = new Thread(si);
				sit.start();
			} catch (IOException e) {
				System.out.println("Accept failed: " + port);
				System.exit(-1);
			}
		}
	}

	protected void finalize(){
		//Objects created in run method are finalized when 
		//program terminates and thread exits
		try{
			server.close();
		} catch (IOException e) {
			System.out.println("Could not close socket");
			System.exit(-1);
		}
	}

	public static void main(String[] args){
		int port = 4444;
		try {
			if (args[0].equals("--help")) {
				System.out.println("Service Interface Listener (v0.1nea)");
				System.out.println("");
				System.out.println("Usage:");
				System.out.println("");
				System.out.println(" -p    Port Number to Listen On");
				System.out.println("");
				System.out.println("");
				System.exit(0);
			} else {
				while (args.length > 0) {
					if (args[0].equals("-p")) {
						port = Integer.parseInt(args[1]);
					}
					if (args[0].equals("-d")) {
						ics_path = String.valueOf(args[1]);
					}
					if (args.length > 2) {
						String[] foo = new String[args.length-2];
						for (int i=2;i<args.length;i++) {
							foo[i-2] = args[i];
						}
						args = foo;
					} else {
						args = new String[0];
					}
				}
			}
		} catch ( Exception e) {
		}
		SocketThrdServer sts = new SocketThrdServer();
		sts.listenSocket(port);
	}
}

